﻿using Business.DB.Interface;
using Business.DB.Orcale;
using Business.DB.SqlServer;
using Bussiness.DB.Model;
using System.Reflection;

namespace MyReflecttion
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");

            #region 普通方法
            ////1.引用
            ////2.new
            ////3.调方法
            ////IDBHelper helple = new SqlServerHelper();
            //IDBHelper helple = new OrcaleHelper();
            //helple.Query();

            //Assembly assembly = Assembly.Load("Business.DB.SqlServer"); ////不用带上dll的后缀,在当前执行目录下面去读取  .NET Framework下是可行的；.net8.0 测试不行
            //Assembly assembly1 = Assembly.LoadFile("F:\\Study\\朝夕教育视频课程\\WPFLesson\\MyReflecttion\\bin\\Debug\\net8.0\\Business.DB.SqlServer.dll"); //需要写全路径，要后缀
            //Assembly assembly2 = Assembly.LoadFrom("Business.DB.SqlServer.dll"); //带上dll的后缀,在当前执行目录下面去读取

            //foreach (var type in assembly.GetTypes())
            //{
            //    Console.WriteLine($"Type.Name={type.FullName}");
            //    foreach (var item in type.GetMethods())
            //    {
            //        Console.WriteLine($"mehtod.Name={item.Name}");
            //    }

            //    foreach (var item in type.GetProperties())
            //    {
            //        Console.WriteLine($"prop.Name={item.Name}");
            //    }
            //}
            //Type typeSql = assembly.GetType("Business.DB.SqlServer.SqlServerHelper"); //写入全名称
            //object? oInstance = Activator.CreateInstance(typeSql);

            //((SqlServerHelper)oInstance).Query();//这种方法可行

            //////oInstance.Query();
            ////// why？ C#是强类型语言，在编译时决定类型；编译器认为这个oInstance 是object 类型； 其实运行时，类型是SqlServerHelper；
            //////只是在编译的时候，编译器不认可；

            ////动态类型--避开编译器的检查
            //dynamic oInstance1 = Activator.CreateInstance(typeSql);
            //oInstance1.Query();

            //#region 用这种动态类型的话可以随便调用 不靠谱
            //////oInstance.Query(); 
            ////////oInstance.Execute();
            ////////oInstance.Get(); 
            //#endregion

            ////类型的转换   这种方法最佳。
            //IDBHelper dBHelper = oInstance as IDBHelper;
            //dBHelper.Query(); 
            #endregion


            #region 架构方法  架构方法是一种反射的高级用法，可以实现动态创建对象，并调用其方法。
            //依赖于抽象(接口)
            IDBHelper dBHelper1 = SimpleFactory.CreateInstance();
            dBHelper1.Query();
            #endregion


            {
                ////反射黑科技 ---反射破坏单例
                //Singleton singleton1 = new Singleton();
                Singleton singleton2 = Singleton.GetInstance();
                //黑科技： 突破了访问修饰符的权限；反射可以突破一切权限；
                Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
                Type type = assembly.GetType("Business.DB.SqlServer.Singleton");
                object oInstanc = Activator.CreateInstance(type, true);
            }

            //五、反射调用方法+反射创建对象的升级篇 
            //反射调用方法，必须要做类型转换后陈才能调用吗？
            //1.我得知道我需要调用的方法名称
            //2.获取到方法的类型   MehtodInfo 的实例
            //3.Invoke方法，执行方法；

            {
                Assembly assembly1 = Assembly.LoadFrom("Business.DB.SqlServer.dll");
                Type type1 = assembly1.GetType("Business.DB.SqlServer.SqlServerHelper");
                object oInstance1 = Activator.CreateInstance(type1);
                MethodInfo methodInfo = type1.GetMethod("Query");
                methodInfo.Invoke(oInstance1, new object[0]);
            }


            //1.反射创建对象的时候，如何给定构造函数的执行
            {
                Assembly assembly2 = Assembly.LoadFrom("Business.DB.SqlServer.dll");
                Type type2 = assembly2.GetType("Business.DB.SqlServer.ReflectionTest");
                //如果要选择构造函数，就直接传入和参数列表匹配的数据即可
                object? oInstance11 = Activator.CreateInstance(type2);
                object? oInstance21 = Activator.CreateInstance(type2, new object[] { "SunnyF··" });
                object? oInstance31 = Activator.CreateInstance(type2, new object[] { 123 });
                object? oInstance41 = Activator.CreateInstance(type2, new object[] { 234, "水手怕水" });
                object? oInstance51 = Activator.CreateInstance(type2, new object[] { "判官", 456 });

            }

            {
                Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
                Type type = assembly.GetType("Business.DB.SqlServer.ReflectionTest");

                object? oInstance1 = Activator.CreateInstance(type);

                {
                    //执行Show1
                    MethodInfo show1 = type.GetMethod("Show1");
                    show1.Invoke(oInstance1, null);

                    //执行Show2
                    MethodInfo show2 = type.GetMethod("Show2");
                    show2.Invoke(oInstance1, new object[] { 3456 }); //传入的参数要和方法的参数完全吻合

                }

                //执行重载方法：
                //因为名称相同的方法有多个，获取方法的名称的时候，需要加上参数类型作为条件去查询；
                //然后还是常规的调用方法的做法；
                {
                    //执行Show3
                    MethodInfo show3 = type.GetMethod("Show3", new Type[] { typeof(int), typeof(string) }); //需要进一步精确查找的是哪个方法；
                    show3.Invoke(oInstance1, new object[] { 34567, "打武松的小老虎~~" }); //传入的参数要和方法的参数完全吻合

                    MethodInfo show31 = type.GetMethod("Show3", new Type[] { typeof(string), typeof(int) }); //需要进一步精确查找的是哪个方法；
                    show31.Invoke(oInstance1, new object[] { "打武松的小老虎~~", 34567 }); //传入的参数要和方法的参数完全吻合 
                    MethodInfo[] methodArray = type.GetMethods();

                }
                //调用私有方法
                {
                    MethodInfo show4 = type.GetMethod("Show4", BindingFlags.Instance | BindingFlags.NonPublic);
                    show4.Invoke(oInstance1, new object[] { "判官" });
                }
                //调用静态方法
                {
                    MethodInfo Show5 = type.GetMethod("Show5");
                    Show5.Invoke(oInstance1, new object[] { "华尔兹" });
                    Show5.Invoke(null, new object[] { "华尔兹" }); //静态类不需要类的实例，就可以执行

                    //ReflectionTest.Show5();//可以直接使用
                }
            }

            //进阶玩法--和泛型相关的
            //泛型的设计思想：
            //延迟声明，声明不确定类型，只给一个占位符，在调用的时候，确定类型；
            {
                Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
                Type type = assembly.GetType("Business.DB.SqlServer.GenericMethod");
                object? oInstance = Activator.CreateInstance(type);
                MethodInfo show = type.GetMethod("Show");
                //invoke 就是去调用，但是这里没有确定类型；
                //show.Invoke(oInstance, new object[] { 123, "SunnyF··" ,DateTime.Now }); //这种方法不可行
                show.MakeGenericMethod(new Type[] { typeof(int), typeof(string), typeof(DateTime) }).Invoke(oInstance, new object[] { 123, "SunnyF··", DateTime.Now }); //这种方法可以
            }

            {
                #region 当一个类是泛型类的时候 这种方法是获取不到type类型的
                //Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
                //Type type = assembly.GetType("Business.DB.SqlServer.GenericClass");
                //object? oInstance = Activator.CreateInstance(type); 
                #endregion
            }

            {
                Console.WriteLine(typeof(GenericClass<,,>));
                Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
                Type type = assembly.GetType("Business.DB.SqlServer.GenericClass`3");
                Type genericType = type.MakeGenericType(new Type[] { typeof(int), typeof(string), typeof(DateTime) });

                object oInstance = Activator.CreateInstance(genericType);
                MethodInfo show = genericType.GetMethod("Show");
                show.Invoke(oInstance, new object[] { 345, "玖", DateTime.Now });
            }

            {
                Assembly assembly = Assembly.LoadFrom("Business.DB.SqlServer.dll");
                Type type = assembly.GetType("Business.DB.SqlServer.GenericDouble`1");
                Type genericType = type.MakeGenericType(new Type[] { typeof(int) });
                object oInstance = Activator.CreateInstance(genericType);
                MethodInfo show = genericType.GetMethod("Show");
                MethodInfo show1 = show.MakeGenericMethod(new Type[] { typeof(string), typeof(DateTime) });
                show1.Invoke(oInstance, new object[] { 1234, "Richard", DateTime.Now });
            }


            //六、反射多种应用场景
            {
                //1. 在Mvc中有应用，请求的路径包含了控制器和方法，通过反射获取控制器的类型，创建实例，根据路径中方法的名称确定要执行的具体的Action 方法； 
                //2. 简单工厂其实是一个IOC容器的雏形~~
                //3. 反射在ORM框架中的使用~ 
                //开发中，反射无处不在~~ 
            }

            //七、反射的局限---性能问题；
            {

                Monitor.Show();
                ////测试用例证明： 反射确实存在性能损失~~
                ////反射，我们不否认他会有性能损失，但是只要程序员的代码足够优秀，性能是可以得到优化的~~
                ////大家放心使用即可~~ 
            }

            {
                //八、反射调用字段属性
                Console.WriteLine("=================================================");
                Console.WriteLine("=================================================");
                Console.WriteLine("常规方案赋值~~~~=====================================");
                People people = new People();
                //给属性字段赋值~~
                people.Id = 123;
                people.Name = "Donnie";
                people.Age = 25;
                people.Description = ".NET高级班的VIP学员~";

                Console.WriteLine("=================================================");
                Console.WriteLine("=================================================");
                Console.WriteLine("常规方案取值~~~~=====================================");
                //取属性字段的值
                Console.WriteLine($"people.Id={people.Id}");
                Console.WriteLine($"people.Name={people.Name}");
                Console.WriteLine($"people.Age={people.Age}");
                Console.WriteLine($"people.Description={people.Description}");

                //1. 增加一个字段，或者是增加一个属性呢？  取值还是赋值，都需要修改代码
                ///   反射操作---至少取值的代码完全不用动的~~
                //反射
                Type type = typeof(People);
                object oInstance = Activator.CreateInstance(type);
                //如何给属性字段赋值呢？
                Console.WriteLine("=================================================");
                Console.WriteLine("=================================================");
                Console.WriteLine("反射赋值~~~~=====================================");
                foreach (PropertyInfo prop in type.GetProperties())
                {
                    if (prop.Name == "Id")
                    {
                        prop.SetValue(oInstance, 123);
                    }
                    else if (prop.Name == "Name")
                    {
                        prop.SetValue(oInstance, "Donnie");
                    }
                    else if (prop.Name == "Age")
                    {
                        prop.SetValue(oInstance, 25);
                    }
                }

                foreach (FieldInfo field in type.GetFields())
                {
                    if (field.Name == "Description")
                    {
                        field.SetValue(oInstance, ".NET高级班的VIP学员~");
                    }
                }


                Console.WriteLine("=================================================");
                Console.WriteLine("=================================================");
                Console.WriteLine("反射取值~~~~=====================================");
                foreach (PropertyInfo prop in type.GetProperties())
                {
                    object ovalue = prop.GetValue(oInstance);
                    Console.WriteLine($"people.{prop.Name}={ovalue}");
                }
                foreach (FieldInfo field in type.GetFields())
                {
                    object ovalue = field.GetValue(oInstance);
                    Console.WriteLine($"people.{field.Name}={ovalue}");
                }


            }


            {
                //九、反射+ADO.NET时间数据库访问层
                //反射在ORM框架中的使用~--ado.net, 可以通过操作实体来完成对于数据库的操作；

                SqlServerHelper helper = new SqlServerHelper();
                Company company = helper.GetCompany(13479);
                //通过实体去操作数据库，只要有实体就可以，实体和数据库表是对应的；

                //如果要查询SysUser呢？ GetUser；
                //根本写不完~~ 数据库中的表，可以有无限多~~  每个表都要写个方法吗？
                //表---对应的是实体-----类---类型；
                //能不能只写一个方法，让其满足不同的实体的需求，一个方法去满足不同类型需求？？？  ？？  泛型；
                {
                    Company company1 = helper.Get<Company>(13479);
                    SysUser user = helper.Get<SysUser>(1);

                }


                ////内部封装，封装好了，对于上端来调用；  只关注类型，只需要把和数据库中的表匹配的 类型传递进去，就可以查询数据；

                {
                    Company company2 = helper.Get<Company>(13479);
                    Company company3 = helper.Get<Company>(13479);
                    Company company4 = helper.Get<Company>(13479);
                    Company company5 = helper.Get<Company>(13479);
                    Company company6 = helper.Get<Company>(13479);
                    SysUser user = helper.Get<SysUser>(1);
                }
            }

            {
                //十、反射生成程序集
                //EMit：
                //程序，是设计定义好，然后通过运行时环境去驱动，去执行定义设计好的程序指令； 
                //EMIT: 在程序运行的过程中，动态的生成一堆新的程序出来---程序造程序； 

                //程序生成程序：
                //程序在运行的时候，要能够动态的生成，程序集，类，结构，变量，方法，属性，字段。。。。。。。。 
                //程序---程序集（dll/exe）--包含---module----包含----类----包含了方法，属性，字段，。。。。。。

                // 
                //ReflectionEmit.Show();
            }
            Console.ReadKey();
        }
    }
}
